import { stat, statSync, existsSync, mkdirSync, readdir, readFileSync, writeFile, readdirSync, copyFile, writeSync, copyFileSync } from "fs";
import path = require("path");
import * as uglify from 'uglify-es';
import { createHash } from "crypto";

export function isIgnoreDir(from: string, ignores?: IIgnoreOption) {
    if (!ignores) {
        return false;
    }
    if (!ignores.dir) {
        return false;
    }
    for (const el of ignores.dir) {
        if (from.match(el)) {
            return true;
        }
    }
    return false;
}
export function isIgnoreFile(from: string, ignores?: IIgnoreOption) {
    if (!ignores) {
        return false;
    }
    if (!ignores.file) {
        return false;
    }
    for (const el of ignores.file) {
        // console.log(from.search(new RegExp(el)));
        // console.log(from, el, from.match(new RegExp(el)));
        if (from.match(new RegExp(el))) {
            return true;
        }
    }
    return false;
}

export interface IIgnoreOption {
    dir?: string[];
    file?: string[];
}
class CpDir {
    private fileNum = 0;
    private callback: Function;
    private ignores: IIgnoreOption | undefined;
    private needHash: boolean = false;

    constructor(ignores: IIgnoreOption | undefined, needHash: boolean, cpOver: Function) {
        this.ignores = ignores;
        this.needHash = needHash;
        this.fileNum = 0;
        this.callback = cpOver;
    }

    public cp(dirPath: string, toDirPath: string) {
        let stat = statSync(dirPath)
        if (stat.isDirectory()) {
            if (isIgnoreDir(dirPath, this.ignores)) {
                return;
            }
            if (!existsSync(toDirPath)) {
                mkdirSync(toDirPath);
            }
            let files = readdirSync(dirPath)
            for (const ifile of files) {
                let ifurl = path.join(dirPath, ifile);
                if (statSync(ifurl).isFile()) {
                    // console.log(isIgnoreFile(ifurl, this.ignores), '要忽略ma');
                    if (isIgnoreFile(ifurl, this.ignores)) {
                        continue;
                    }
                    this.fileNum++;
                    // console.log('add: ', this.fileNum);
                }
                this.cp(ifurl, path.join(toDirPath, ifile));
            }
        } else {

            let f = readFileSync(dirPath);
            let realFilePath = this.getRealFilePath(toDirPath, f);
            writeFile(realFilePath, f, (err3) => {
                if (err3) {
                    console.log(err3);
                    console.log(dirPath);
                    console.log(`拷贝文件出错${realFilePath}`);
                    return;
                }
                this.fileNum--;
                // console.log('now: ', this.fileNum);

                if (this.fileNum === 0) {
                    if (this.callback !== null) {
                        this.callback.call(null);
                    }
                }
            });
        }

    }

    private getRealFilePath(oldPath: string, fileData?: Buffer) {
        /* if (this.needHash) {
            return hashFile(oldPath, fileData);
        } else {
            return oldPath;
        } */
        return oldPath;
    }
}

export function cpDir(dirPath: string, toDirPath: string, callback: () => void,
    hash: boolean = false, ignores?: IIgnoreOption) {

    if (!existsSync(toDirPath)) {
        mkdirsSync(toDirPath);
    }
    let cpdir = new CpDir(ignores, hash, callback);
    cpdir.cp(dirPath, toDirPath);

}

export function mkdirsSync(dirname) {
    if (existsSync(dirname)) {
        return true;
    } else {
        if (mkdirsSync(path.dirname(dirname))) {
            mkdirSync(dirname);
            return true;
        }
    }
}

export function cpFile(from: string[], to: string[], callback: () => void, uglify = false, ignores?: IIgnoreOption) {

    let cpFile = new CpFile(from, to, callback, uglify, ignores);
    cpFile.cp();
}

class CpFile {

    protected _from: string[];
    protected _to: string[];
    protected _callback: () => void;
    protected _idx = 0;
    protected _uglify = false;
    protected _ignores: IIgnoreOption;

    constructor(from: string[], to: string[], callback: () => void, uglify = false, ignores?: IIgnoreOption) {
        this._uglify = uglify;
        this._from = from;
        this._to = to;
        this._callback = callback;
        this._idx = 0;
        this._ignores = ignores;
    }

    cp() {
        if (this._idx >= this._from.length) {
            if (!!this._callback) this._callback();
        } else {
            let from = this._from[this._idx]
            let to = this._to[this._idx];

            if (isIgnoreFile(from, this._ignores)) {
                this._idx++;
                this.cp();
                return;
            }
            createDirSync(path.dirname(to), () => {

                if (this._uglify) {

                    let txt = readFileSync(from).toString();
                    let rst = uglify.minify(txt, {
                        mangle: true
                    });

                    writeFile(to, rst.code, () => {
                        this._idx++;
                        this.cp();
                    })
                } else {

                    copyFile(from, to, () => {
                        this._idx++;
                        this.cp();
                    })
                }

            })
        }
    }
}

export function createDirSync(dirPath: string, callback?: () => void) {
    if (existsSync(dirPath)) {
        if (!!callback) {
            callback();
        }
    } else {
        createDirSync(path.dirname(dirPath), () => {
            mkdirSync(dirPath);
            if (!!callback) {
                callback();
            }
        });
    }
}


/**
 * 通过文件的路径或者内容, 得到hash之后的文件名
 * @param filePath 文件的真实路径
 * @param fileData 文件的内容
 */
export function hashFile(filePath: string, fileData?: Buffer) {
    let md5 = createHash('md5');
    if (!fileData) {
        fileData = readFileSync(filePath);
    }
    md5.update(fileData);
    let md5Str = md5.digest('hex');
    let fnext = path.extname(filePath);
    let fnHead = path.basename(filePath, fnext);
    let fn = fnHead + '_' + md5Str.substr(0, 8) + fnext;
    let realFn = path.join(path.dirname(filePath), fn);
    return realFn;
}